`include "OperBloc.sv"

// OperCarryBloc:
// the module that builds the recursive structure of the multi-operand full-adder.
module OperCarryBloc #(
  parameter WIDTH = 4,
  parameter LEVEL = 2,
  parameter OPERS = 1 << LEVEL,
  parameter HALFS = OPERS >> 1
)
(
  input  logic [OPERS-1:0][WIDTH-1:0] Op,  // array of operands 
  input  logic [WIDTH-1:0]            Si,  // incoming prev sum bits
  input  logic [WIDTH-2:0]            Ci,  // incoming carry bits
  input  logic [LEVEL-1:0]            Cie, // incoming extended carry bits (from previous bloc)
  output logic [WIDTH-1:0]            So,  // outgoing sum bits
  output logic [WIDTH-2:0]            Co,  // outgoing carry bits
  output logic [LEVEL  :0]            Coe  // outgiong extended carry bits (to next bloc, 1-bit gt than cie)
);
// LEVEL 0: Fallback to Simple FA array

generate if (LEVEL == 0) begin
  OperBloc#(.WIDTH(WIDTH)) ob(.A(Op[0]), .B(Si), .C({Ci, 1'b0}), .S(So), .Co({Coe, Co}));
  `ifdef FORMAL
    always_comb ADV: assert(Op[0] + Si + {Ci, 1'b0} == {Coe, Co, 1'b0} + So);
  `endif
end 
// LEVEL 1: Two OperBloc connected
else if (LEVEL == 1) begin

  wire logic cm1, cm2;
  wire logic [WIDTH-2:0] Cm;
  wire logic [WIDTH-1:0] Sm;

  OperBloc#(.WIDTH(WIDTH)) op1(.A(Op[0]), .B(Si), .C({Ci, 1'b0}), .S(Sm), .Co({cm1, Cm}));
  OperBloc#(.WIDTH(WIDTH)) op2(.A(Op[1]), .B(Sm), .C({Cm, 1'b0}), .S(So), .Co({cm2, Co}));

  assign Coe = Cie + cm1 + cm2;

end
// LEVEL 2+: Recursive instantiation of the OperCarryBloc, dividing the operand matrix into upper and lower parts, and passing them to the lower level construction.
else begin

  wire logic [LEVEL-1:0] Cm1, Cm2;
  wire logic [WIDTH-2:0] Cm;
  wire logic [WIDTH-1:0] Sm;

  wire logic [(OPERS >> 1)-1:0][WIDTH-1:0] opU, opL;
  assign opU = Op[    0 +: HALFS];
  assign opL = Op[HALFS +: HALFS];

  OperCarryBloc#(.WIDTH(WIDTH), .LEVEL(LEVEL-1)) 
    ocbU(.Op(opU), .Cie(Cie[LEVEL-2:0]), .Coe(Cm1), .So(Sm), .Co(Cm), .*);

  OperCarryBloc#(.WIDTH(WIDTH), .LEVEL(LEVEL-1))
    ocbL(.Op(opL), .Cie(Cm1[LEVEL-2:0]), .Coe(Cm2), .Si(Sm), .Ci(Cm), .*);

  assign Coe[LEVEL:LEVEL-1] = Cie[LEVEL-1] + Cm1[LEVEL-1] + Cm2[LEVEL-1];
  assign Coe[LEVEL-2:0]     = Cm2[LEVEL-2:0];

end
endgenerate

endmodule
